☕ Java I/O Streams and File Handling

Complete Guide with Examples, Best Practices, and Interview Preparation

✅ 1. What is Java I/O?

"I/O" means Input and Output. Java I/O is used to perform read and write operations with various sources like files, networks, keyboards, etc.

📥 Input

Reading data from keyboard, file, network, etc.

Example: Reading from a file

📤 Output

Writing data to file, screen, network, etc.

Example: Writing to a file

Java I/O is part of the java.io package and provides abstractions for reading and writing data.

✅ 2. Java Streams Concept

A stream is like a pipe or channel that flows data in a sequence. Streams can be chained together for more complex operations.

InputStream

Used to read data

OutputStream

Used to write data

✅ 3. Types of Streams

Java has 2 types of streams:

Stream Type What it handles Use for
Byte Stream Handles binary data (8 bits) Images, PDFs, audio, any non-text data
Character Stream Handles text data (16 bits Unicode) Text files, strings, handling character encoding
More on Byte vs Character Streams

Byte streams are low-level and operate on raw bytes, while character streams handle encoding and decoding automatically, making them suitable for internationalized text.

✅ 3.1 Stream Class Hierarchy

Understanding the class hierarchy helps in choosing the right stream for the task. Here's an interactive diagram (hover to zoom):

Java IO Stream Hierarchy Diagram 1

Java Byte Stream Hierarchy

Java IO Stream Hierarchy Diagram 2

Java I/O Class Hierarchy

Java IO Stream Hierarchy Diagram 3

Java Character Stream Hierarchy

Java IO Stream Hierarchy Diagram 4

Java Object Stream Hierarchy

🔹 Important Stream Methods

Class Method Description Return Type
InputStream read() Reads the next byte of data int
InputStream read(byte[]) Reads bytes into an array int
InputStream available() Returns an estimate of available bytes to read int
InputStream skip(long) Skips over and discards specified bytes long
InputStream close() Closes the input stream void
OutputStream write(int) Writes a single byte void
OutputStream write(byte[]) Writes an array of bytes void
OutputStream flush() Flushes any buffered data to the output void
OutputStream close() Closes the output stream void
Reader read() Reads a single character int
Reader read(char[]) Reads characters into an array int
Reader close() Closes the reader void
Writer write(int) Writes a single character void
Writer write(char[]) Writes an array of characters void
Writer write(String) Writes a string void
Writer flush() Flushes buffered data to the output void
Writer close() Closes the writer void
BufferedReader readLine() Reads a line of text String
BufferedWriter newLine() Writes a line separator void
✅ 4. Byte Streams (For Binary Data)

Byte streams read/write 1 byte at a time. They are abstract classes: InputStream and OutputStream.

🔹 Read a file using Byte Stream

ReadByteExample.java
import java.io.*;

public class ReadByteExample {
    public static void main(String[] args) throws Exception {
        FileInputStream fis = new FileInputStream("data.txt");
        int i;
        while ((i = fis.read()) != -1) {
            System.out.print((char) i); // convert byte to char
        }
        fis.close();
    }
}

🔹 Write to a file using Byte Stream

WriteByteExample.java
import java.io.*;

public class WriteByteExample {
    public static void main(String[] args) throws Exception {
        FileOutputStream fos = new FileOutputStream("data.txt");
        String data = "Hello from Java!";
        fos.write(data.getBytes()); // convert String to byte[]
        fos.close();
    }
}
Using try-with-resources for Auto-closing
import java.io.*;

public class ReadByteWithResources {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("data.txt")) {
            int i;
            while ((i = fis.read()) != -1) {
                System.out.print((char) i);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

This ensures streams are closed automatically, preventing resource leaks.

✅ 5. Character Streams (For Text Data)

Character streams read/write characters (2 bytes). Abstract classes: Reader and Writer.

🔹 Read a file using Character Stream

ReadCharExample.java
import java.io.*;

public class ReadCharExample {
    public static void main(String[] args) throws Exception {
        FileReader fr = new FileReader("data.txt");
        int i;
        while ((i = fr.read()) != -1) {
            System.out.print((char)i);
        }
        fr.close();
    }
}

🔹 Write to a file using Character Stream

WriteCharExample.java
import java.io.*;

public class WriteCharExample {
    public static void main(String[] args) throws Exception {
        FileWriter fw = new FileWriter("data.txt");
        fw.write("Welcome to Java I/O!");
        fw.close();
    }
}
Example with PrintWriter for formatted output
import java.io.*;

public class PrintWriterExample {
    public static void main(String[] args) throws Exception {
        PrintWriter pw = new PrintWriter(new FileWriter("data.txt"));
        pw.println("Hello");
        pw.printf("Formatted: %d %s", 10, "items");
        pw.close();
    }
}
✅ 6. Buffered Streams (Fast Reading/Writing)

Buffered streams make input/output faster by reducing the number of system calls through buffering data in memory.

🔹 BufferedReader for fast reading

BufferedReadExample.java
import java.io.*;

public class BufferedReadExample {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new FileReader("data.txt"));
        String line;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
        br.close();
    }
}

🔹 BufferedWriter for fast writing

BufferedWriteExample.java
import java.io.*;

public class BufferedWriteExample {
    public static void main(String[] args) throws Exception {
        BufferedWriter bw = new BufferedWriter(new FileWriter("data.txt"));
        bw.write("Efficient writing!");
        bw.newLine();
        bw.close();
    }
}
✅ 7. File Handling (Creating, Reading, Writing, Deleting Files)

Java has a File class to handle files and directories.

🔹 Important File Class Methods

Method Description Return Type
createNewFile() Creates a new empty file if it doesn't exist boolean
delete() Deletes the file or directory boolean
exists() Checks if the file or directory exists boolean
getName() Returns the name of the file or directory String
getAbsolutePath() Returns the absolute pathname string String
length() Returns the size of the file in bytes long
listFiles() Returns an array of files in the directory File[]
mkdir() Creates a directory boolean
mkdirs() Creates a directory and any necessary parent directories boolean
canRead() Checks if the file is readable boolean
canWrite() Checks if the file is writable boolean
isFile() Checks if the path is a regular file boolean
isDirectory() Checks if the path is a directory boolean
lastModified() Returns the last modified time in milliseconds long
setReadOnly() Sets the file to read-only boolean

Create a file

CreateFileExample.java
import java.io.*;

public class CreateFileExample {
    public static void main(String[] args) throws IOException {
        File file = new File("demo.txt");
        if (file.createNewFile()) {
            System.out.println("File created");
        } else {
            System.out.println("File already exists");
        }
    }
}

Write to a file

FileWriteExample.java
import java.io.*;

public class FileWriteExample {
    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("demo.txt");
        fw.write("This is a file handling example.");
        fw.close();
    }
}

Read from a file using Scanner

FileReadScanner.java
import java.io.*;
import java.util.*;

public class FileReadScanner {
    public static void main(String[] args) throws FileNotFoundException {
        File file = new File("demo.txt");
        Scanner sc = new Scanner(file);
        while (sc.hasNextLine()) {
            System.out.println(sc.nextLine());
        }
        sc.close();
    }
}

Delete a file

DeleteFileExample.java
import java.io.*;

public class DeleteFileExample {
    public static void main(String[] args) {
        File file = new File("demo.txt");
        if (file.delete()) {
            System.out.println("File deleted");
        } else {
            System.out.println("File not deleted");
        }
    }
}

Create a directory

CreateDirectoryExample.java
import java.io.*;

public class CreateDirectoryExample {
    public static void main(String[] args) {
        File dir = new File("mydir");
        if (dir.mkdir()) {
            System.out.println("Directory created");
        } else {
            System.out.println("Directory already exists or failed");
        }
    }
}

List files in a directory

ListFilesExample.java
import java.io.*;

public class ListFilesExample {
    public static void main(String[] args) {
        File dir = new File(".");
        File[] files = dir.listFiles();
        for (File file : files) {
            System.out.println(file.getName());
        }
    }
}
✅ 8. Serialization (Save Object to File)

Serialization = Saving object to file. Deserialization = Reading object from file.

Make your class implement Serializable

Student.java
import java.io.*;

class Student implements Serializable {
    int id;
    String name;
    public Student(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

Serialize (save object)

import java.io.*;

public class SerializeExample {
    public static void main(String[] args) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("student.ser"));
        Student s = new Student(1, "Deepak");
        oos.writeObject(s);
        oos.close();
    }
}

Deserialize (read object)

import java.io.*;

public class DeserializeExample {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("student.ser"));
        Student s = (Student) ois.readObject();
        System.out.println(s.id + " " + s.name);
        ois.close();
    }
}
Transient and SerialVersionUID

Use transient keyword to exclude fields from serialization. serialVersionUID is used for version control during deserialization.

class Student implements Serializable {
    private static final long serialVersionUID = 1L;
    transient int temp; // not serialized
    // ...
}
✅ 8.1 Common I/O Exceptions
Exception Description
IOException General I/O error
FileNotFoundException File does not exist
EOFException End of file reached unexpectedly
InvalidClassException Issue during deserialization
✅ 8.2 Best Practices
  • Always close streams in finally block or use try-with-resources (Java 7+).
  • Use buffered streams for performance.
  • Handle exceptions properly.
  • For large files, read in chunks.
  • Use NIO for non-blocking I/O in high-performance apps.
✅ 8.3 Introduction to Java NIO (Advanced)

Java NIO (New I/O) is an alternative to standard I/O, introduced in Java 1.4. It provides non-blocking I/O, channels, buffers, and selectors.

Key classes: Path, Files, ByteBuffer, Channel.

🔹 Read file using NIO

import java.nio.file.*;
import java.io.IOException;

public class NIOReadExample {
    public static void main(String[] args) throws IOException {
        Path path = Paths.get("data.txt");
        byte[] bytes = Files.readAllBytes(path);
        System.out.println(new String(bytes));
    }
}
Difference between IO and NIO

IO is stream-oriented and blocking, NIO is buffer-oriented and non-blocking.

✅ 9. Summary Table
Task Class Used
Read file (bytes) FileInputStream
Write file (bytes) FileOutputStream
Read file (chars) FileReader
Write file (chars) FileWriter
Fast reading BufferedReader
Create/Delete File File
Read file line-wise Scanner
Save object ObjectOutputStream
Load object ObjectInputStream
NIO File Operations Files, Path
✅ 10. Most Important Interview Questions

🎯 Key Interview Questions & Answers

1. Difference between FileReader and BufferedReader?

• FileReader reads char by char.

• BufferedReader reads full line, faster, and buffers data for efficiency.

2. What is Serialization?

• Saving an object's state into a file or stream. The class must implement Serializable interface.

3. What's the difference between byte and character stream?

• Byte stream → binary data, operates on 8-bit bytes.

• Character stream → text data, operates on 16-bit Unicode characters, handles encoding.

4. How do you read large files efficiently?

• Use BufferedReader or BufferedInputStream to reduce I/O operations.

• Read in chunks using byte arrays.

5. What is the purpose of transient keyword in serialization?

• It marks fields that should not be serialized, like sensitive data or derived fields.

6. Explain try-with-resources.

• Introduced in Java 7, it automatically closes resources like streams after the try block, even if exceptions occur.

7. Difference between Java IO and NIO?

• IO is blocking and stream-oriented.

• NIO is non-blocking, buffer-oriented, and supports selectors for scalability.

8. How to copy a file in Java?

• Use Files.copy(source, target) in NIO, or manually read from InputStream and write to OutputStream.

9. What is ObjectOutputStream and ObjectInputStream?

• Used for serialization and deserialization of objects.

10. How to handle FileNotFoundException?

• It's a subclass of IOException. Check if the file exists before opening, or catch and handle gracefully.

11. What are Filter Streams?

• They add functionality to other streams, like DataInputStream for reading primitives.

12. Explain RandomAccessFile.

• Allows reading and writing at any position in the file using seek().

13. How to get the size of a file?

• Use File.length() or Files.size(path).

14. What is PipedInputStream and PipedOutputStream?

• Used for communication between threads, like pipes.

15. Why use Scanner for input?

• It can parse primitive types and strings easily from input sources.